// This program is hand-edited starting from the file:

// v1model-multiple-resubmit-reasons-hand-edited.p4

// It introduces a switch statement to P4 like C's switch statement,
// just for brevity.  It could be easily replaced with a daisy chain
// of if-then-else-if-... and have the same behavior.

// It also introdues an untagged union, which does not exist in P4_16.
// I am intending to use it in a safe manner, such that a tagged union
// could also be used instead.  That is, the intent of having a union
// is _not_ to coerce one type into another, but simply to explicitly
// tell the compiler that we only need storage for one of the members
// of the union, not all of them.

// It changes the type of argument of resubmit() from a list of fields
// to a value of type bit<8>, which is simply an identifier for which
// field list you want to preserve for the resubmitted packet.

#include <core.p4>
#include <v1model.p4>

struct mymeta_t {
    bit<3>   resubmit_reason;
    bit<128> f1;
    bit<160> f2;
    bit<256> f3;
    bit<64>  f4;
}

header ethernet_t {
    bit<48> dstAddr;
    bit<48> srcAddr;
    bit<16> etherType;
}

struct metadata {
    mymeta_t mymeta;
}

// NEW: One struct type per P4_14 field_list that was used as an
// argument to a resubmit operation.

struct resubmit_fl1_t {
    bit<3>   resubmit_reason;
    bit<128> f1;
}

struct resubmit_fl2_t {
    bit<3>   resubmit_reason;
    bit<160> f2;
}

struct resubmit_fl3_t {
    bit<3>   resubmit_reason;
    bit<256> f3;
}

struct resubmit_fl4_t {
    bit<3>   resubmit_reason;
    bit<64>  f4;
}

// NEW: One combined struct that includes a union of all of the
// possible resubmit field lists, plus an integer id to specify which
// ones of the union members is valid, or 0 if none of them is valid
// because the packet was not the result of a resubmit operation.

struct resubmit_meta_t {
    bit<3> resubmit_field_list_id;
    union {
        resubmit_fl1_t resubmit_fl1;
        resubmit_fl2_t resubmit_fl2;
        resubmit_fl3_t resubmit_fl3;
        resubmit_fl4_t resubmit_fl4;
    }
}

struct headers {
    ethernet_t ethernet;
}

parser ParserImpl(packet_in packet,
    out headers hdr,
    inout metadata meta,
    inout standard_metadata_t standard_metadata,
    // NEW: in parameter
    in resubmit_meta_t resubmit_meta)
{
    state start {
        // NEW: Code auto-generated by P4_14 to P4_16 translation.
        // Users writing code for P4_16+v1model would be given code
        // like this as an example to follow.
        switch (resubmit_meta.resubmit_field_list_id) {
            1: {
                meta.resubmit_reason = resubmit_meta.resubmit_fl1.resubmit_reason;
                meta.f1 = resubmit_meta.resubmit_fl1.f1;
            }
            2: {
                meta.resubmit_reason = resubmit_meta.resubmit_fl2.resubmit_reason;
                meta.f2 = resubmit_meta.resubmit_fl2.f2;
            }
            3: {
                meta.resubmit_reason = resubmit_meta.resubmit_fl3.resubmit_reason;
                meta.f3 = resubmit_meta.resubmit_fl3.f3;
            }
            4: {
                meta.resubmit_reason = resubmit_meta.resubmit_fl4.resubmit_reason;
                meta.f4 = resubmit_meta.resubmit_fl4.f4;
            }
            default: {
                // nothing to do here.  This packet was not the result
                // of a resubmit operation.
            }
        }
        transition parse_ethernet;
    }
    state parse_ethernet {
        packet.extract(hdr.ethernet);
        transition accept;
    }
}

control egress(inout headers hdr,
    inout metadata meta,
    inout standard_metadata_t standard_metadata)
{
    apply {
    }
}

control ingress(inout headers hdr,
    inout metadata meta,
    inout standard_metadata_t standard_metadata,
    // NEW: out parameter
    out resubmit_meta_t resubmit_meta)
{
    action set_port_to_mac_da_lsbs() {
        standard_metadata.egress_spec = (bit<9>) hdr.ethernet.dstAddr & 0xf;
    }
    action do_resubmit_reason1() {
        meta.mymeta.resubmit_reason = 1;
        meta.mymeta.f1 = meta.mymeta.f1 + 17;
        // NEW: resubmit() calls take an integer, not a list of
        // fields.  The _only_ effect this has is to immediately copy
        // this integer into a per-packet hidden field.  In the
        // current BMv2 simple_switch implementation, this field isn't
        // even hidden -- it is field 'resubmit_flag' in the standard
        // metadata structure.

        // This integer value will be used later near the end of the
        // ingress control.  See there for details.
        resubmit(1);
    }
    action do_resubmit_reason2(bit<160> f2_val) {
        meta.mymeta.resubmit_reason = 2;
        meta.mymeta.f2 = f2_val;
        // NEW: resubmit() calls take an integer
        resubmit(2);
    }
    action nop() {
    }
    action my_drop() {
        mark_to_drop(standard_metadata);
    }
    action do_resubmit_reason3() {
        meta.mymeta.resubmit_reason = 3;
        meta.mymeta.f3 = (bit<256>) hdr.ethernet.srcAddr;
        meta.mymeta.f3 = meta.mymeta.f3 + (bit<256>) hdr.ethernet.dstAddr;
        // NEW: resubmit() calls take an integer
        resubmit(3);
    }
    action do_resubmit_reason4() {
        meta.mymeta.resubmit_reason = 4;
        meta.mymeta.f4 = (bit<64>) hdr.ethernet.etherType;
        // NEW: resubmit() calls take an integer
        resubmit(4);
    }
    action update_metadata(bit<64> x) {
        meta.mymeta.f1 = meta.mymeta.f1 - 2;
        meta.mymeta.f2 = 8;
        meta.mymeta.f3 = (bit<256>) hdr.ethernet.etherType;
        meta.mymeta.f4 = meta.mymeta.f4 + x;
    }
    table t_first_pass_t1 {
        actions = {
            set_port_to_mac_da_lsbs;
            do_resubmit_reason1;
            do_resubmit_reason2;
            @defaultonly nop;
        }
        key = {
            hdr.ethernet.srcAddr: ternary;
        }
        default_action = nop();
    }
    table t_first_pass_t2 {
        actions = {
            my_drop;
            do_resubmit_reason3;
            do_resubmit_reason4;
        }
        key = {
            hdr.ethernet.dstAddr: ternary;
        }
        default_action = my_drop();
    }
    table t_first_pass_t3 {
        actions = {
            update_metadata;
            @defaultonly nop;
        }
        key = {
            hdr.ethernet.etherType: ternary;
        }
        default_action = nop();
    }
    table t_second_pass_reason1 {
        actions = {
            my_drop;
            nop;
            set_port_to_mac_da_lsbs;
        }
        key = {
            meta.mymeta.f1: exact;
        }
        default_action = nop();
    }
    table t_second_pass_reason2 {
        actions = {
            nop;
            do_resubmit_reason1;
        }
        key = {
            meta.mymeta.f2: ternary;
        }
        default_action = nop();
    }
    table t_second_pass_reason3 {
        actions = {
            my_drop;
            set_port_to_mac_da_lsbs;
            @defaultonly nop;
        }
        key = {
            meta.mymeta.f3        : exact;
            hdr.ethernet.etherType: exact;
        }
        default_action = nop();
    }
    table t_second_pass_reason4 {
        actions = {
            my_drop;
            nop;
            set_port_to_mac_da_lsbs;
        }
        key = {
            meta.mymeta.f4      : ternary;
            hdr.ethernet.srcAddr: exact;
        }
        default_action = nop();
    }
    apply {
        if (meta.mymeta.resubmit_reason == 0) {
            // This is a new packet, not a resubmitted one
            t_first_pass_t1.apply();
            // Note that even if a resubmit() call was performed while
            // executing an action for table t_first_pass_t1, we may
            // do another resubmit() call while executing an action
            // for table t_first_pass_t2.  In general, the last
            // resubmit() call should have its field list preserved.
            // Each resubmit() call causes any earlier resubmit()
            // calls made during the same execution of the ingress
            // control to be "forgotten".
            t_first_pass_t2.apply();
            // Since in this imagined version of the program, we are
            // only passing integer id values to the resubmit() calls,
            // and waiting until the end of the ingress control to
            // actually preserve the desired list of fields, we are
            // not introducing new P4_16 language semantics in order
            // to preserve those final values of the fields.
            
            // recirculate and clone operations, and perhaps also
            // digest calls (TBD what their "timing" is for copying
            // the field values is relative to the call to
            // generate_digest() in P4_14), should be handled
            // similarly in P4_16, in order to match their P4_14
            // behavior.
            t_first_pass_t3.apply();
        }
        else if (meta.mymeta.resubmit_reason == 1) {
            t_second_pass_reason1.apply();
        } else if (meta.mymeta.resubmit_reason == 2) {
            t_second_pass_reason2.apply();
        } else if (meta.mymeta.resubmit_reason == 3) {
            t_second_pass_reason3.apply();
        } else {
            t_second_pass_reason4.apply();
        }

        // NEW: Code auto-generated by P4_14 to P4_16 translation.
        // Users writing code for P4_16+v1model would be given code
        // like this as an example to follow.  Of course if someone
        // were writing it by hand, they would probably eliminate the
        // resubmit_reason field and just use resubit_field_list_id
        // instead, but the code below is intended to be an
        // auto-translated result from the P4_14 program, where the
        // compiler would have no way in general of knowing that such
        // a code change was correct.

        // NEW: resubmit_field_list_id() is a new extern function that
        // returns 0 if no resubmit operation was performed during
        // ingress, or the value passed to the most recent resubmit()
        // extern function call that was executed.

        // Note that it would be nice if the source code made it
        // straightforward for a compiler to implement this in such a
        // way that the "encoding" of the preserved metadata fields
        // could vary in size across the different cases.  This could
        // improve average performance of a system where attaching
        // extra data to a resubmitted packet was a significant cost.

        // For example, if resubmit_field_list_id is 4, only 64+3 bits
        // of metadata field data need to be preserved with the
        // resubmitted packet, whereas if it is 3, 256+3 bits need to
        // be preserved with the packet.  If case 4 is frequent enough
        // relative to case 3, there could be a performance advantage
        // to optimizing case 3 _not_ to preserve 64+3 bits, plus
        // (256-64)=192 bits of "padding".
        resubmit_meta.resubmit_field_list_id = resubmit_field_list_id();
        switch (resubmit_meta.resubmit_field_list_id) {
            1: {
                resubmit_meta.resubmit_fl1.resubmit_reason = meta.resubmit_reason;
                resubmit_meta.resubmit_fl1.f1 = meta.f1;
            }
            2: {
                resubmit_meta.resubmit_fl2.resubmit_reason = meta.resubmit_reason;
                resubmit_meta.resubmit_fl2.f2 = meta.f2;
            }
            3: {
                resubmit_meta.resubmit_fl3.resubmit_reason = meta.resubmit_reason;
                resubmit_meta.resubmit_fl3.f3 = meta.f3;
            }
            4: {
                resubmit_meta.resubmit_fl4.resubmit_reason = meta.resubmit_reason;
                resubmit_meta.resubmit_fl4.f4 = meta.f4;
            }
            default: {
                // Nothing to do here.  No resubmit() call was made
                // while ingress processing was done for this packet,
                // at least not in this pass.
            }
        }
    }
}

control DeparserImpl(packet_out packet, in headers hdr) {
    apply {
        packet.emit(hdr.ethernet);
    }
}

control verifyChecksum(inout headers hdr, inout metadata meta) {
    apply {
    }
}

control computeChecksum(inout headers hdr, inout metadata meta) {
    apply {
    }
}

V1Switch(ParserImpl(), verifyChecksum(), ingress(),
    egress(), computeChecksum(), DeparserImpl()) main;

